﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Npgsql;
using elmah2.core.loggers;
using elmah2.core.data;
using System.Data;
using elmah2.core.encoders;

namespace elmah2.Postgre
{
  public class PostgreLogger : DBLogger
  {
    public override IDbCommand GetErrorsCommand(int page, int numPerPage, int? app)
    {
      StringBuilder sb = new StringBuilder();
      NpgsqlCommand command = new NpgsqlCommand();
      sb.Append("SELECT *, (SELECT COUNT(*) FROM \"Errors\" as count) as Count FROM \"Errors\" ");

      if (app.HasValue)
      {
        sb.Append(" WHERE \"Application\" = @app");
        command.Parameters.AddWithValue("@app", app.Value);
      }

      sb.Append(" offset @skip limit @take");

      command.Parameters.AddWithValue("@skip", numPerPage * (page - 1));
      command.Parameters.AddWithValue("@take", numPerPage * (page));
      command.CommandText = sb.ToString();
      return command;
    }

    public override QueryData LoadApplications()
    {
      IDbCommand command = GetCommand();
      command.CommandText = "SELECT * FROM \"Applications\"";
      return ExecuteCommandQuery(command);
    }

    public override Error GetError(string id)
    {
      Error toReturn = null;
      IDbCommand command = new NpgsqlCommand();
      command.CommandText = "SELECT \"Details\" from \"Errors\" where \"ErrorId\" = @id";
      AddParmToCommand(command, "@id", id);
      QueryData qd = ExecuteCommandQuery(command);
      if (qd.QuerySuccessful)
      {
        toReturn = XmlEncoder.DecodeString(qd.GetDataForRowField(0, "Details").ToString());
      }

      return toReturn;
    }

    public override bool AddApplication(string name)
    {
      List<Application> apps = GetApplications();
      Application found = apps.Where(R => R.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();

      if (found == null)
      {
        IDbCommand command = new NpgsqlCommand();
        command.CommandText = "INSERT INTO \"Applications\"(\"ApplicationName\") Values(@name)";
        AddParmToCommand(command, "@name", name);
        ExecuteCommand(command);
        _apps = null;
        return true;
      }
      return false;
    }

    public override void LogError(Error error)
    {
      Application app = null;
      do
      {
        app = GetApplications().Where(R => R.Name.Equals(error.ApplicationName)).FirstOrDefault();
        if (app == null)
          AddApplication(error.ApplicationName);
      }
      while (app == null);


      string id = Guid.NewGuid().ToString();
      if (!init) throw new Exception("Please init before logging data");

      IDbCommand command = GetCommand();
      command.CommandText = "INSERT INTO \"Errors\"(\"ErrorId\", \"Application\", \"Source\", \"Host\", \"Type\", \"Message\", \"UserName\", \"StatusCode\", \"TimeUtc\", \"Details\") Values(@id, @app, @source, @host, @type, @msg, @user, @status, @time, @detail)";

      AddParmToCommand(command, "@id", id);
      AddParmToCommand(command, "@app", app.ID);
      AddParmToCommand(command, "@host", error.HostName);
      AddParmToCommand(command, "@source", error.Source);
      AddParmToCommand(command, "@type", error.Type);
      AddParmToCommand(command, "@msg", error.Message);
      AddParmToCommand(command, "@user", error.User);
      AddParmToCommand(command, "@status", error.StatusCode);
      AddParmToCommand(command, "@time", error.Time.ToUniversalTime());
      AddParmToCommand(command, "@detail", error.Encode());

      ExecuteCommand(command);
    }

    public override IDbConnection GetConnection()
    {
      return new NpgsqlConnection(_connectionString);
    }

    public override IDbCommand GetCommand()
    {
      return new NpgsqlCommand();
    }
  }
}
